﻿//     Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using System.Net;
using System.Web;
using System.Web.Mvc;
using Gateway.Console.Models;
using Microsoft.Gateway.Configuration;

namespace Gateway.Console.Controllers
{
    public class RoleController : Controller
    {
        private ConsoleContext db = new ConsoleContext();

        // GET: /Role/
        public async Task<ActionResult> Index()
        {
            var roles = await db.RoleModels.ToListAsync();
            var defaultRole = roles.FirstOrDefault(role => role.IsDefaultRole);
            if (defaultRole == null)
            {
                roles.Add(ConstructDefaultRole());
            }
            return View(roles);
        }

        // GET: /Role/Create
        public ActionResult Create()
        {
            var newRole = new RoleModel
            {
                Flights = new FlightModel[] { new FlightModel { Weight = 100 }}.ToList(),
            };
            return View("Edit", FixupRoleCollections(newRole));
        }

        // POST: /Role/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Create(RoleModel rolemodel)
        {
            FixupRoleCollections(rolemodel);
            if (ModelState.IsValid)
            {
                await CreateOrUpdateRole(rolemodel);
                return RedirectToAction("Index");
            }
            return View("Edit", rolemodel);
        }

        // GET: /Role/Edit/5
        public async Task<ActionResult> Edit(int? id)
        {
            RoleModel roleModel = null;
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            else if (id.Value == 0)
            {
                roleModel = ConstructDefaultRole();
            }
            else
            {
                roleModel = await db.RoleModels.FindAsync(id.Value);
            }
            if (roleModel == null)
            {
                return HttpNotFound();
            }
            return View(roleModel);
        }

        // POST: /Role/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Edit(RoleModel rolemodel)
        {
            FixupRoleCollections(rolemodel);
            if (ModelState.IsValid)
            {
                await CreateOrUpdateRole(rolemodel);
                return RedirectToAction("Index");
            }
            return View(rolemodel);
        }

        // GET: /Role/Delete/5
        public async Task<ActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            RoleModel rolemodel = await db.RoleModels.FindAsync(id.Value);
            if (rolemodel == null)
            {
                return HttpNotFound();
            }
            return View(rolemodel);
        }

        // POST: /Role/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> DeleteConfirmed(int? id)
        {
            RoleModel rolemodel = await db.RoleModels.FindAsync(id.Value);
            db.RoleModels.Remove(rolemodel);
            await db.SaveChangesAsync();
            return RedirectToAction("Index");
        }

        private RoleModel ConstructDefaultRole()
        {
            return new RoleModel
            {
                IsDefaultRole = true,
                RoleName = Role.ROLE_NAME_DEFAULT,
                Flights = new FlightModel[] { new FlightModel { Weight = 100 }}.ToList(),
                Restrictions = new List<RestrictionsModel>(),
            };
        }

        private async Task CreateOrUpdateRole(RoleModel role)
        {
            if (role.Id == 0)
            {
                db.RoleModels.Add(role);
            }
            else
            {
                var currentRole = await db.RoleModels.FindAsync(role.Id);
                ReconcileChildCollection(db.FlightModels, currentRole.Flights, role.Flights, flight => flight.Id, (dbFlight, formFlight) =>
                    {
                        dbFlight.Weight = formFlight.Weight;
                        dbFlight.Redirect = formFlight.Redirect;
                        dbFlight.Addresses = formFlight.Addresses;
                        dbFlight.Users = formFlight.Users;
                        dbFlight.Tenants = formFlight.Tenants;
                        db.Entry(dbFlight).State = EntityState.Modified;
                    });
                ReconcileChildCollection(db.RestrictionModels, currentRole.Restrictions, role.Restrictions, restriction => restriction.Id, (dbRestriction, formRestriction) =>
                {
                    dbRestriction.RestrictBelow = formRestriction.RestrictBelow;
                    db.Entry(dbRestriction).State = EntityState.Modified;
                });
                currentRole.RoleName = role.RoleName;
                db.Entry(currentRole).State = EntityState.Modified;
            }
            await db.SaveChangesAsync();
        }

        private void ReconcileChildCollection<T>(DbSet<T> childEntitySet, ICollection<T> existingChildren, ICollection<T> newChildren, Func<T, int> keyExtractor, Action<T, T> childCloner) where T : class
        {
            var newItems = new HashSet<int>(newChildren.Select(item => keyExtractor(item)));
            var deletedItems = existingChildren
                .Where(currentItem => !newItems.Contains(keyExtractor(currentItem)));
            childEntitySet.RemoveRange(deletedItems);
            foreach (var newItem in newChildren.Where(newItem => keyExtractor(newItem) == 0))
            {
                existingChildren.Add(newItem);
                childEntitySet.Add(newItem);
            }
            foreach (var existingItem in newChildren.Where(newItem => keyExtractor(newItem) != 0))
            {
                int id = keyExtractor(existingItem);
                var existingChild = existingChildren.FirstOrDefault(item => keyExtractor(item) == id);
                if (existingChild != null)
                {
                    childCloner(existingChild, existingItem);
                }
            }
        }

        private RoleModel FixupRoleCollections(RoleModel role)
        {
            if (role.Flights == null)
            {
                role.Flights = new List<FlightModel>();
            }
            if (role.Restrictions == null)
            {
                role.Restrictions = new List<RestrictionsModel>();
            }
            return role;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}
